"""Tiny HTTP Proxy. """

__version__ = "0.2.1"

import sys
import BaseHTTPServer, select, socket, SocketServer, urlparse
from browser.http_server import RCHTTPRequestHandler

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

class ProxyHandler (RCHTTPRequestHandler):
    __base = BaseHTTPServer.BaseHTTPRequestHandler
    __base_handle = __base.handle

    rbufsize = 0                        # self.rfile Be unbuffered

    def handle(self):
        (ip, port) =  self.client_address
        # Only localhost can use this proxy...
        if not ip.startswith('127.'):
            self.raw_requestline = self.rfile.readline()
            if self.parse_request(): self.send_error(403)
        else:
            self.__base_handle()

    def _connect_to(self, netloc, soc):
        i = netloc.find(':')
        if i >= 0:
            host_port = netloc[:i], int(netloc[i+1:])
        else:
            host_port = netloc, 80
        try: soc.connect(host_port)
        except socket.error, arg:
            try: msg = arg[1]
            except: msg = arg
            self.send_error(404, msg)
            return 0
        return 1

    def do_CONNECT(self):
        soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            if self._connect_to(self.path, soc):
                self.log_request(200)
                self.wfile.write(self.protocol_version +
                                 " 200 Connection established\r\n")
                self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
                self.wfile.write("\r\n")
                self._read_write(soc, 300)
        finally:
            soc.close()
            self.connection.close()

    def do_GET(self):
        (scm, netloc, path, params, query, fragment) = urlparse.urlparse(
            self.path, 'http')

        if path.startswith('/chitika-rc'):
            return self.local_handle()

        if scm != 'http' or fragment or not netloc:
            self.send_error(400, "bad url %s" % self.path)
            return
        soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            if self._connect_to(netloc, soc):
                self.log_request()
                soc.send("%s %s %s\r\n" % (
                    self.command,
                    urlparse.urlunparse(('', '', path, params, query, '')),
                    self.request_version))

                self.headers['Connection'] = 'close'
                del self.headers['Proxy-Connection']
                for key, val in self.headers.items():
                    key = key.title()
                    if key == 'Accept-Encoding':
                        continue
                    if key == 'Host':
                        val = netloc
                    soc.send("%s: %s\r\n" % (key, val))
                soc.send("\r\n")
                try:
                    self._read_write(soc)
                except:
                    pass
        finally:
            soc.close()
            self.connection.close()

    def _read_write(self, soc, max_idling=20):
        iw = [self.connection, soc]
        ow = []
        count = 0
        while 1:
            count += 1
            (ins, _, exs) = select.select(iw, ow, iw, 3)
            if exs: break
            if ins:
                for i in ins:
                    out_to_client = True
                    if i is soc:
                        out = self.connection
                    else:
                        out_to_client = False
                        out = soc
                    if out_to_client:
                        data = i.recv(8192).replace('top.location', 'self.location').replace('window.top', 'window.self')
                    else:
                        data = i.recv(8192)

                    if data:
                        out.send(data)
                        count = 0

            if count == max_idling: break

    do_HEAD = do_GET
    do_POST = do_GET
    do_PUT  = do_GET
    do_DELETE=do_GET

class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
    pass
